﻿using System;
using System.Threading.Tasks;
using BASIC_CORE.Configuration.Caching;
using BASIC_CORE.Domain.Caching.DBContext;
using Microsoft.Extensions.Options;
using ServiceStack.Redis;

namespace BASIC_CORE.Storage.Caching.DBContext
{
    public class RedisDbContext : IRedisDbContext
    {
        /// <summary>
        /// Redis连接池管理实例
        /// </summary>
        private readonly PooledRedisClientManager _pooledClientManager;

        /// <summary>
        /// 构造函数
        /// </summary>
        public RedisDbContext(IOptions<ServiceStackRedisOptions> _optionsAccessor)
        {

            if (_optionsAccessor == null)
            {
                throw new ArgumentNullException(nameof(_optionsAccessor));
            }

            var options = _optionsAccessor.Value;

            if (options.ReadWriteServers == null && options.ReadOnlyServers == null &&
                string.IsNullOrEmpty(options.SingleServer))
            {
                throw new ArgumentNullException(nameof(options.SingleServer));
            }

            if (options.ReadWriteServers?.Length < 1 && options.ReadOnlyServers?.Length < 1 &&
                string.IsNullOrEmpty(options.SingleServer))
            {
                throw new ArgumentNullException(nameof(options.SingleServer));
            }

            var config = new RedisClientManagerConfig
            {
                AutoStart = true,
                MaxWritePoolSize = options.MaxWritePoolSize,
                MaxReadPoolSize = options.MaxReadPoolSize,
                DefaultDb = options.DefaultDb,
            };
            _pooledClientManager = new PooledRedisClientManager(
                options.ReadWriteServers ?? new[] { options.SingleServer }
                , options.ReadOnlyServers ?? new[] { options.SingleServer },
                config)
            {
                ConnectTimeout = options.ConnectTimeout,
                SocketSendTimeout = options.SendTimeout,
                SocketReceiveTimeout = options.ReceiveTimeout,
                IdleTimeOutSecs = options.IdleTimeOutSecs,
                PoolTimeout = options.PoolTimeout
            };
        }


        /// <summary>
        /// 获取Redis客户端连接对象，有连接池管理。
        /// </summary>
        /// <param name="isReadOnly">是否取只读连接。Get操作一般是读，Set操作一般是写</param>
        /// <returns></returns>
        public async Task<RedisClient> GetRedisClient(bool isReadOnly = true)
        {
            RedisClient result;
            if (!isReadOnly)
            {
                //RedisClientManager.GetCacheClient()会返回一个新实例，而且只提供一小部分方法，它的作用是帮你判断是否用写实例还是读实例
                result = await _pooledClientManager.GetCacheClientAsync() as RedisClient;
            }
            else
            {
                //如果你读写是两个做了主从复制的Redis服务端，那么要考虑主从复制是否有延迟。有一些读操作是否是即时的，需要在写实例中获取。
                result = await _pooledClientManager.GetReadOnlyClientAsync() as RedisClient;
            }
            //如果你的需求需要经常切换Redis数据库，则下一句可以用。否则一般都只用默认0数据库，集群是没有数据库的概念。
            //result.ChangeDb(Db);
            return result;
        }

    }
}
